/*******************************************************************************
 * Copyright (c) 2010 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.eci.xmi;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.legstar.eci.xsd.EciXsdAnnotator;
import com.legstar.xsd.InvalidParameterException;
import com.legstar.xsd.XsdMappingException;

/**
 * ECI XMI to COBOL-annotated XML schema executable.
 * <p/>
 * This is the main class for the executable jar. It takes options from the
 * command line and calls the {@link EciXsdAnnotator} API.
 * <p/>
 * Usage: <code>
 * java -jar legstar-ecixmi-x.y.z-exe.jar -i&lt;input file or folder&gt; -o&lt;output folder&gt;
 * </code>
 * 
 */
public class EciXmiToXsdMain {

    /** The version properties file name. */
    private static final String VERSION_FILE_NAME = "/com/legstar/eci/xmi/version.properties";

    /** Execution parameters set. */
    private EciXmiToXsdModel _model;

    /** Logger. */
    private final Log _log = LogFactory.getLog(getClass());

    /**
     * Construct using a parameter set.
     * 
     * @param model the parameter set
     */
    public EciXmiToXsdMain(final EciXmiToXsdModel model) {
        _model = model;
    }

    /**
     * @param args translator options. Provides help if no arguments passed.
     */
    public static void main(final String[] args) {
        EciXmiToXsdMain main = new EciXmiToXsdMain(new EciXmiToXsdModel());
        main.execute(args);
    }

    /**
     * Process command line options and run translator.
     * <p/>
     * If no options are passed, prints the help. Help is also printed if the
     * command line options are invalid.
     * 
     * @param args translator options
     */
    public void execute(final String[] args) {
        try {
            Options options = createOptions();
            if (collectOptions(options, args)) {
                execute();
            }
        } catch (Exception e) {
            _log.error("ECI to COBOL-annotated XML schema translation failure",
                    e);
            throw new RuntimeException(e);
        }
    }

    /**
     * @return the command line options
     */
    protected Options createOptions() {
        Options options = new Options();

        Option version = new Option("v", "version", false,
                "print the version information and exit");
        options.addOption(version);

        Option help = new Option("h", "help", false,
                "print the options available");
        options.addOption(help);

        Option input = new Option("i", "input", true,
                "file or folder holding the ECI WSDL files to translate."
                        + " Name is relative or absolute");
        options.addOption(input);

        Option encoding = new Option("e", "encoding", true,
                "Character set used for ECI WSDL files encoding");
        options.addOption(encoding);

        Option eciWsdlFileFilter = new Option("f", "eciWsdlFileFilter", true,
                "Wildcard filter for ECI XMI WSDL binding files");
        options.addOption(eciWsdlFileFilter);

        Option output = new Option("o", "output", true,
                "folder or file receiving the COBOL-annotated XML schema");
        options.addOption(output);

        return options;
    }

    /**
     * Take arguments received on the command line and setup corresponding
     * options.
     * <p/>
     * No arguments is valid. It means use the defaults.
     * 
     * @param options the expected options
     * @param args the actual arguments received on the command line
     * @return true if arguments were valid
     * @throws Exception if something goes wrong while parsing arguments
     */
    protected boolean collectOptions(final Options options, final String[] args)
            throws Exception {
        if (args != null && args.length > 0) {
            CommandLineParser parser = new PosixParser();
            CommandLine line = parser.parse(options, args);
            return processLine(line, options);
        }
        return true;
    }

    /**
     * Process the command line options selected.
     * 
     * @param line the parsed command line
     * @param options available
     * @return false if processing needs to stop, true if its ok to continue
     * @throws Exception if line cannot be processed
     */
    protected boolean processLine(final CommandLine line, final Options options)
            throws Exception {
        if (line.hasOption("version")) {
            System.out.println("version " + getVersion());
            return false;
        }
        if (line.hasOption("help")) {
            produceHelp(options);
            return false;
        }
        if (line.hasOption("input")) {
            getModel().setInputEciWsdlFile(
                    new File(line.getOptionValue("input").trim()));
        }
        if (line.hasOption("encoding")) {
            getModel().setInputEciWsdlFileEncoding(
                    line.getOptionValue("encoding").trim());
        }
        if (line.hasOption("eciWsdlFileFilter")) {
            getModel().setInputEciWsdlFileFilter(
                    line.getOptionValue("eciWsdlFileFilter").trim());
        }
        if (line.hasOption("output")) {
            getModel().setTargetXsdFile(
                    new File(line.getOptionValue("output").trim()));
        }

        return true;
    }

    /**
     * @param options options available
     * @throws Exception if help cannot be produced
     */
    protected void produceHelp(final Options options) throws Exception {
        HelpFormatter formatter = new HelpFormatter();
        String version = getVersion();
        formatter.printHelp(
                "java -jar legstar-ecixmi-"
                        + version.substring(0, version.indexOf(' '))
                        + "-exe.jar followed by:", options);
    }

    /**
     * Translate a single file or all binding files from an input folder. Place
     * results in the output folder.
     * <p/>
     * 
     * @throws IOException if basic read/write operation fails
     * @throws XsdMappingException if translation fails
     * @throws InvalidParameterException if one of the parameters is invalid
     */
    protected void execute() throws XsdMappingException,
            InvalidParameterException, IOException {
        EciXmiToXsdIO eci2xsd = new EciXmiToXsdIO(getModel());
        eci2xsd.execute();
    }

    /**
     * Pick up the version from the properties file.
     * 
     * @return the product version
     * @throws IOException if version cannot be identified
     */
    protected String getVersion() throws IOException {
        InputStream stream = null;
        try {
            Properties version = new Properties();
            stream = EciXmiToXsdMain.class
                    .getResourceAsStream(VERSION_FILE_NAME);
            version.load(stream);
            return version.getProperty("version");
        } finally {
            if (stream != null) {
                stream.close();
            }
        }
    }

    /**
     * @return the execution parameters set
     */
    public EciXmiToXsdModel getModel() {
        return _model;
    }

}
